package edu.stanford.bmir.protege.web.server.stkosutil.enricher;


import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
import java.util.Vector;

import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.EntityType;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.semanticweb.owlapi.vocab.OWLRDFVocabulary;

import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.UtilConstants;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.CandidateEntity;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.EnricherSource;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.EnrichmentOptions;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.EnrichmentResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.OriginEntity;
import edu.stanford.bmir.protege.web.server.change.ChangeApplicationResult;
import edu.stanford.bmir.protege.web.server.change.ChangeDescriptionGenerator;
import edu.stanford.bmir.protege.web.server.change.FixedMessageChangeDescriptionGenerator;
import edu.stanford.bmir.protege.web.server.owlapi.AssertedClassHierarchyProvider;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProject;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProjectManager;
import edu.stanford.bmir.protege.web.server.owlapi.RenameMap;
import edu.stanford.bmir.protege.web.server.owlapi.RenderingManager;
import edu.stanford.bmir.protege.web.server.stkosutil.common.AppException;
import edu.stanford.bmir.protege.web.shared.project.ProjectId;
import edu.stanford.bmir.protege.web.shared.user.UserId;

public class ClassEnricher {
	private RefOntology _refOntology = null;
	public List<CandidateEntity> fetchCandidateEntities(String projectName,
			OriginEntity originEntity, EnrichmentOptions options) {
		try{
			KnowlegeBaseClient client = KnowlegeBaseClient.getInstance();
			long thesaurusId = options.getRefThesaurusID();
			ArrayList<Long> alThesaurusIdList = new ArrayList<Long>();
			if (thesaurusId!=UtilConstants.UNKNOWN_INT_VALUE){
				alThesaurusIdList.add(new Long(thesaurusId));
			}
			List<CandidateEntity> result = client.fetchCandidateChildClasses(originEntity.getBrowserText(),alThesaurusIdList);
			return result;
		}catch(Exception ex){
			ex.printStackTrace();
			return null;
		}
	}
	
	 private OWLAPIProject getProject(String projectName) {
	        if (projectName == null) {
	            throw new NullPointerException("projectName must not be null");
	        }
	        ProjectId projectId = ProjectId.get(projectName);
	        // TODO: Log
	        return getProject(projectId);
	    }

	    /**
	     * Gets the OWLAPIProject for a given {@link ProjectId}.If a project with the specified id exists then that project
	     * will be returned, otherwise, a fresh project will be created and that fresh project returned.
	     * @param projectId The id of the project.
	     * @return The OWL API project. Not <code>null</code>.
	     */
	    private OWLAPIProject getProject(ProjectId projectId) {
	        OWLAPIProjectManager pm = OWLAPIProjectManager.getProjectManager();
	        return pm.getProject(projectId);
	    }
	 //*******************   
	
	    
	 private List<CandidateClassHolder> getSubclasses(OWLAPIProject project, String className) {
	        if (className == null) {
	            return Collections.emptyList();
	        }
	        List<CandidateClassHolder> result = new ArrayList<CandidateClassHolder>();
	       
	        RenderingManager rm = project.getRenderingManager();
	        AssertedClassHierarchyProvider hierarchyProvider = project.getClassHierarchyProvider();
	        OWLClass cls = rm.getEntity(className, EntityType.CLASS);

	        boolean checkForDeprecated = project.getRootOntology().containsAnnotationPropertyInSignature(OWLRDFVocabulary.OWL_DEPRECATED.getIRI());
	        for (OWLClass subclass : new ArrayList<OWLClass>(hierarchyProvider.getChildren(cls))) {
	            boolean deprecated = false;
//	            if(checkForDeprecated) {
//	                deprecated = project.isDeprecated(subclass);
//	            }
	        //if (!deprecated) {
	              //  Set<OWLClass> children = hierarchyProvider.getChildren(subclass);
	              //  int subClassSubClassesCount = children.size();
	                String browserText = rm.getBrowserText(subclass);
	                String name = subclass.getIRI().toString();
	                CandidateClassHolder classHolder = new CandidateClassHolder();;
	                classHolder.setBrowserText(browserText);
	                classHolder.setName(name);
	                classHolder.setOwlClass(subclass);
	                //data.setDeprecated(deprecated);

	                //data.setValueType(ValueType.Cls);
	                result.add(classHolder);
//	            }
	        }
	        Collections.sort(result, new Comparator<CandidateClassHolder>() {
	            public int compare(CandidateClassHolder o1, CandidateClassHolder o2) {
//	                if(o1.isDeprecated()) {
//	                    if(!o2.isDeprecated()) {
//	                        return 1;
//	                    }
//	                }
//	                else if(o2.isDeprecated()) {
//	                    return -1;
//	                }
	                String browserText1 = o1.getBrowserText();
	                String browserText2 = o2.getBrowserText();
	                if(browserText1.startsWith("'")) {
	                    browserText1 = browserText1.substring(1);
	                }
	                if(browserText2.startsWith("'")) {
	                    browserText2 = browserText2.substring(1);
	                }
	                return browserText1.compareToIgnoreCase(browserText2);
	            }
	        });
	        return result;
	    }
	 	private List<CandidateClassHolder> loadAllClassList(OWLAPIProject project){
	 		 List<CandidateClassHolder> result = new ArrayList<CandidateClassHolder>();
	 		 Stack<CandidateClassHolder> sClassHolders = new Stack<CandidateClassHolder>();
	 		 CandidateClassHolder root = this.getRootClass(project);
	 		 sClassHolders.push(root);
	 		 while (!sClassHolders.empty()){
	 			 CandidateClassHolder currentClass = sClassHolders.pop();
	 			 List<CandidateClassHolder> children = this.getSubclasses(project, currentClass.getName());
	 			 Collections.reverse(children);
	 			 for (CandidateClassHolder child: children){
	 				 sClassHolders.push(child);
	 			 }
	 			 result.add(currentClass);		 
	 		 }
	 		 return result;
	 	}
	 
	 	private CandidateClassHolder getRootClass(OWLAPIProject project) {
	 	     CandidateClassHolder holder = new CandidateClassHolder();
	 	     RenderingManager rm = project.getRenderingManager();
	 	     OWLClass owlThing = project.getDataFactory().getOWLThing();
	 	     String browserText = rm.getBrowserText(owlThing);
          String name = owlThing.getIRI().toString();
          holder.setBrowserText(browserText);
          holder.setName(name);
	 	     holder.setOwlClass(owlThing);
	 	     return holder;
	 	}
	 	public EnrichmentResult enrichOntology(String projectName, EnrichmentOptions options, UserId userId) throws AppException{
	 		if (options.getSource()==EnricherSource.REF_PROJECT){
	 			OWLAPIProject refProject = this.getProject(options.getRefProjectName());
	 			this._refOntology = new RefOntology(refProject,options.getMatchLevel());
	 			_refOntology.loadAllClasses();
	 		}
	 		Vector<String> vExistingNames = new Vector<String>();
	 		EnrichmentResult result = new EnrichmentResult();
	 		List<CandidateClassHolder> allClassList = null;
	 		ArrayList<CandidateClassHolder> classWithCandidateList = new ArrayList<CandidateClassHolder>();
	 		OWLAPIProject project = this.getProject(projectName);
	 		
	 		allClassList= this.loadAllClassList(project);
	 		for (CandidateClassHolder classHolder: allClassList){
	 			String lowerCaseName = classHolder.getBrowserText().toLowerCase();
	 			int index = Collections.binarySearch(vExistingNames, lowerCaseName);
	            if (index<0){
	            	vExistingNames.insertElementAt(lowerCaseName, (index + 1) * -1);
	            }
	 		}
	 		for (CandidateClassHolder classHolder: allClassList){
	 			if (classHolder.getName().equals(OWLRDFVocabulary.OWL_THING.getIRI().toString())){
	 				continue;
	 			}
	 			ArrayList<CandidateEntity> lCandidateChildClassList = this.fetchCandidateChildClasses(classHolder.getBrowserText(), options);
	 			if (lCandidateChildClassList!=null && lCandidateChildClassList.size()>0){
		 			for (int i=lCandidateChildClassList.size()-1; i>=0; i--){
		 				String lowerCaseName = lCandidateChildClassList.get(i).getBrowserText().toLowerCase();
		 				int index = Collections.binarySearch(vExistingNames, lowerCaseName);
			            if (index>=0){
			            	lCandidateChildClassList.remove(i);
			            }else{
			            	vExistingNames.insertElementAt(lowerCaseName, (index + 1) * -1);
			            }
		 			}
	 			}
	 			if (lCandidateChildClassList!=null && lCandidateChildClassList.size()>0){
	 				classHolder.setCandidateChildClassList(lCandidateChildClassList);
	 				classWithCandidateList.add(classHolder);
	 			}
	 		}
	 			 		
	 		CandidateClassesChangeGenerator changeGenerator = new CandidateClassesChangeGenerator(classWithCandidateList);
	 		ChangeDescriptionGenerator descriptionGenerator = new FixedMessageChangeDescriptionGenerator("本体丰富");
	 		ChangeApplicationResult result2 =  this.getProject(projectName).applyChanges(userId, changeGenerator, descriptionGenerator);
	 		//RenameMap renameMap = result2.getRenameMap();
	 		HashMap<OWLClass, String> hFreshClassList = changeGenerator.getFresshClassList();
	 		OWLDataFactory df = project.getDataFactory();
	 		List<OWLOntologyChange> changeList = new ArrayList<OWLOntologyChange>();
	 	
	 		for (Entry<OWLClass, String> entry : hFreshClassList.entrySet()) {
	 			OWLClass freshClass = entry.getKey();
	 			String from = entry.getValue();
	 			OWLEntity renamedClass = result2.getRenamedEntity(freshClass);
	 			String annotaionValue= UtilConstants.FLAG_ENRICHED + ": Y" + "; " + UtilConstants.FLAG_FROM + ": " + from;
	 			OWLAnnotation commentAnno = df.getOWLAnnotation(
	 					 df.getRDFSComment(),
	 					 df.getOWLLiteral(annotaionValue, "en"));
	 			
	 			OWLAxiom ax = df.getOWLAnnotationAssertionAxiom(renamedClass.getIRI(), commentAnno);
	 			AddAxiom addAxiom =  new AddAxiom(project.getRootOntology(), ax);	 
	 			changeList.add(addAxiom);
	 		}
	 		this.getProject(projectName).applyChanges(userId, changeList, "添加annotaion");
	 		
	 		return result;
	 	}
	 	
	 	public ArrayList<CandidateEntity> fetchCandidateChildClasses(String browserText, EnrichmentOptions options) throws AppException {
	 		ArrayList<CandidateEntity> alCandidateEntities = null;
	 		if (options.getSource() == EnricherSource.REF_PROJECT){
	 			return _refOntology.fetchCandidateChildClasses(browserText);
	 		}else if (options.getSource() == EnricherSource.THESAURUS){
	 			KnowlegeBaseClient client = new KnowlegeBaseClient();
	 			ArrayList<Long> alRefThesaurusIdList = null;
	 			if (options.getRefThesaurusID()!=UtilConstants.UNKNOWN_INT_VALUE){
	 				alRefThesaurusIdList = new ArrayList<Long>(); 
	 				alRefThesaurusIdList.add(options.getRefThesaurusID());
	 			}else{
	 				alRefThesaurusIdList=null;
	 			}
	 			alCandidateEntities = client.fetchCandidateChildClasses(browserText, alRefThesaurusIdList);
	 			return alCandidateEntities;
	 		}else{
	 			return alCandidateEntities;
	 		}
	 	}
}
